#encoding:utf-8

# from __future__ import print_function

import sys
import xattr
import logging
import struct



EXTENTD_ATTR_NAME = 'system.cifs_acl'
CIFS_ACE_HEAD_LEN = 8
CIFS_SID_HEAD_LEN = 8


logger = logging.getLogger('root')

def get_cifs_acl_attr(filepath):
    """
        获取文件扩展属性
    """
    try:
        return xattr.getxattr(filepath, EXTENTD_ATTR_NAME)
    except IOError as ex:
        logger.error("Get object=%s Rights Failed, errmsg:%s", filepath, str(ex))

def parse_cifs_acl_head(attr_value):
    """
        解析cifs_acl头数据
        @attr_value acl数据流

        header:
            uint16_t revision
            uint16_t type
            uint32_t osidoffset
            uint32_t gsidoffset
            uint32_t sacloffset
            uint32_t dacloffset

    """
    fmt = 'HHIIII'
    byte_len = struct.calcsize(fmt)
    s = struct.Struct(fmt)
    revision, ctype, own_offset, group_offset, _, dacl_offset = s.unpack(
        attr_value[:byte_len])
    return revision, ctype, own_offset, group_offset, dacl_offset

def parse_sid(attr_value, offset, cifs_ace_len):
    """
        解析sid
        @offset 偏移量
        @cifs_sid_len sid结构体长度

        header：
            uint8_t revision
            uint8_t num_subauth
            uint8_t authority[6]
            uint32_t sub_auth[15] 最大长度15
    """
    # 获取cifs_sid偏移量
    cifs_sid_len = cifs_ace_len - CIFS_ACE_HEAD_LEN

    # 构造解包格式
    fmt = 'BB6B'+ 'I' * ((cifs_sid_len - CIFS_SID_HEAD_LEN) / struct.calcsize('I'))
    s = struct.Struct(fmt)

    cifs_sid = s.unpack(attr_value[offset:offset + cifs_sid_len])
    revision = str(cifs_sid[0])
    sid = "S-" + revision

    # 获取 num_subauth
    num_subauth = cifs_sid[1]

    # 获取 authority
    val = cifs_sid[7]
    val += cifs_sid[6] << 8
    val += cifs_sid[5] << 16
    val += cifs_sid[4] << 24
    val += cifs_sid[3] << 32
    val += cifs_sid[2] << 40
    sid += "-" + str(val)

    # 获取 sub_auth
    for i in range(num_subauth):
        sid += "-%s" % str(cifs_sid[8 + i])
    return sid

def parse_dace(attr_value, offset):
    """
        获取dace

        header：
            uint16_t revision
            uint16_t size
            uint32_t num_aces
    """
    fmt = 'HHI'
    dace_len = struct.calcsize(fmt)
    s = struct.Struct(fmt)

    _, _, num_aces = s.unpack(attr_value[offset:offset + dace_len])

    for _ in range(num_aces):
        dace_size, dace_offset, ace_list = parse_ace(attr_value, offset + dace_len)
        dace_len = dace_size
        offset = dace_offset
        yield ace_list

def parse_ace(attr_value, offset):
    """
        获取ace

        header:
            uint8_t type
            uint8_t flags
            uint16_t size
            uint32_t access_req
            cifs_sid sid
    """
    fmt = 'BBHI'
    ace_len = struct.calcsize(fmt)
    s = struct.Struct(fmt)
    stype, flags, size, access_req = s.unpack(attr_value[offset:offset + ace_len])
    sid = parse_sid(attr_value, offset + ace_len, size)
    ace_list = [sid, hex(stype), hex(flags), hex(access_req)]
    return size, offset, ace_list

def get_acl_data_by_filepath(filepath):
    attr_value = get_cifs_acl_attr(filepath)
    data = []
    if attr_value:
        _, _, _, _, dacl_offset = parse_cifs_acl_head(attr_value)
        for i in parse_dace(attr_value, dacl_offset):
            data.append(i)
    return data


# def main(filepath):
#     attr_value = get_cifs_acl_attr(filepath)
#     if attr_value:
#         revision, ctype, own_offset, group_offset, dacl_offset = parse_cifs_acl_head(attr_value)
#         print("REVISION:0x%x" % revision)
#         print("CONTROL:0x%x" % ctype)

#         owner_sid = parse_sid(attr_value, own_offset, 68)
#         group_sid = parse_sid(attr_value, group_offset, 68)

#         print("OWNER:" + owner_sid)
#         print("GROUP:" + group_sid)

#         for i in parse_dace(attr_value, dacl_offset):
#             print(i)


# if __name__ == '__main__':
#     main(sys.argv[1])
